/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.diplomka.datalayer.daoimpl;

import cz.muni.fi.diplomka.datalayer.dao.MessageDao;
import cz.muni.fi.diplomka.datalayer.entity.MessageEntity;
import cz.muni.fi.diplomka.datalayer.util.DbConnector;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.graphdb.Transaction;

/**
 *
 * @author Jakub Kutil
 */
public class MessageDaoImpl implements MessageDao {

    @Override
    public void addMessage(MessageEntity messageEntity) {
        if (messageEntity == null) {
            throw new IllegalArgumentException("Message Entity can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            String query = "MATCH (Message:Message) RETURN max(Message.id)";

            Long key = new Long(0);
            ExecutionResult result = engine.execute(query);
            Iterator<Long> number = result.columnAs("max(Message.id)");
            while (number.hasNext()) {
                key = number.next();
            }

            if (key == null) {
                key = new Long(0);
            }
            messageEntity.setId(new Long(key + 1));

            params.put("id", messageEntity.getId());
            params.put("playerFrom", messageEntity.getIdFrom());
            params.put("playerTo", messageEntity.getIdTo());
            params.put("subject", messageEntity.getSubject().toString());
            params.put("body", messageEntity.getBody().toString());
            params.put("time", messageEntity.getTime().getTime());
            params.put("unread", messageEntity.isUnread());

            query = "MATCH (playerFrom:Player {id : {playerFrom}}), (playerTo:Player {id : {playerTo}}) "
                    + "CREATE (Message: Message {id : {id}, subject : {subject}, "
                    + "body : {body}, time : {time}, unread : {unread}})<-[r:SENT]-(playerFrom) "
                    + "CREATE (Message)<-[r2:RECIEVED]-(playerTo)";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public void editMessage(MessageEntity messageEntity) {
        if (messageEntity == null) {
            throw new IllegalArgumentException("Message Entity can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("id", messageEntity.getId());
            params.put("subject", messageEntity.getSubject().toString());
            params.put("body", messageEntity.getBody().toString());
            params.put("time", messageEntity.getTime().getTime());
            params.put("unread", messageEntity.isUnread());

            String query = "MATCH (Message:Message { id :{id}})"
                    + "SET Message.subject = {subject}, Message.body = {body},"
                    + "Message.time = {time}, Message.unread = {unread}";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public void deleteMessage(Long id) {
        if (id == null) {
            throw new IllegalArgumentException("Id can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("id", id);

            String query = "MATCH (Message:Message{ id : {id}})-[r]-() DELETE Message,r";
            engine.execute(query, params);
            t.success();
        }
    }

    @Override
    public MessageEntity findMessage(Long id) {
        if (id == null) {
            throw new IllegalArgumentException("Id can not be null.");
        }
        MessageEntity messageEntity = new MessageEntity();
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {
            Map<String, Object> params = new HashMap<>();

            params.put("id", id);

            String query = "MATCH (PlayerFrom:Player)-[r:SENT]->(Message:Message {id :{id} })<-[r2:RECIEVED]-(PlayerTo:Player)"
                    + "RETURN Message,PlayerFrom.id,PlayerTo.id";

            ExecutionResult result = engine.execute(query, params);

            ResourceIterator<Map<String, Object>> messages = result.iterator();

            while (messages.hasNext()) {
                Map<String, Object> row = messages.next();
                Node node = (Node) row.get("Message");

                messageEntity.setId(new Long(node.getProperty("id").toString()));
                messageEntity.setIdFrom(new Long(row.get("PlayerFrom.id").toString()));
                messageEntity.setIdTo(new Long(row.get("PlayerTo.id").toString()));
                messageEntity.setBody(node.getProperty("body").toString());
                messageEntity.setSubject(node.getProperty("subject").toString());
                messageEntity.setTime(new Timestamp(Long.valueOf(node.getProperty("time").toString())));
                messageEntity.setUnread(Boolean.valueOf(node.getProperty("unread").toString()));
            }

            t.success();
        }
        return messageEntity;
    }

    @Override
    public List<MessageEntity> getAllReceivedMessagesOfPlayer(Long playerId) {
        if (playerId == null) {
            throw new IllegalArgumentException("Player Id can not be null.");
        }
        List<MessageEntity> messageList = new ArrayList<>();
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {
            Map<String, Object> params = new HashMap<>();

            params.put("id", playerId);

            String query = "MATCH (PlayerTo:Player {id : {id}})-[r:RECIEVED]->(Message:Message)<-[r2:SENT]-(PlayerFrom)"
                    + "RETURN Message,PlayerFrom.id";

            ExecutionResult result = engine.execute(query, params);

            ResourceIterator<Map<String, Object>> messages = result.iterator();

            while (messages.hasNext()) {
                Map<String, Object> row = messages.next();
                Node node = (Node) row.get("Message");

                MessageEntity messageEntity = new MessageEntity();
                messageEntity.setId(new Long(node.getProperty("id").toString()));
                messageEntity.setIdFrom(new Long(row.get("PlayerFrom.id").toString()));
                messageEntity.setIdTo(playerId);
                messageEntity.setBody(node.getProperty("body").toString());
                messageEntity.setSubject(node.getProperty("subject").toString());
                messageEntity.setTime(new Timestamp(Long.valueOf(node.getProperty("time").toString())));
                messageEntity.setUnread(Boolean.valueOf(node.getProperty("unread").toString()));

                messageList.add(messageEntity);
            }


            t.success();
        }
        return messageList;
    }

    @Override
    public List<MessageEntity> getAllSentMessagesOfPlayer(Long playerId) {
        if (playerId == null) {
            throw new IllegalArgumentException("Player Id can not be null.");
        }
        List<MessageEntity> messageList = new ArrayList<>();
        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();


        try (Transaction t = gds.beginTx()) {
            Map<String, Object> params = new HashMap<>();

            params.put("id", playerId);

            String query = "MATCH (PlayerFrom:Player {id : {id} })-[r:SENT]->(Message:Message)<-[r2:RECIEVED]-(PlayerTo:Player)"
                    + "RETURN Message,PlayerTo.id";

            ExecutionResult result = engine.execute(query, params);

            ResourceIterator<Map<String, Object>> messages = result.iterator();

            while (messages.hasNext()) {
                Map<String, Object> row = messages.next();
                Node node = (Node) row.get("Message");

                MessageEntity messageEntity = new MessageEntity();
                messageEntity.setId(new Long(node.getProperty("id").toString()));
                messageEntity.setIdFrom(playerId);
                messageEntity.setIdTo(new Long(row.get("PlayerTo.id").toString()));
                messageEntity.setBody(node.getProperty("body").toString());
                messageEntity.setSubject(node.getProperty("subject").toString());
                messageEntity.setTime(new Timestamp(Long.valueOf(node.getProperty("time").toString())));
                messageEntity.setUnread(Boolean.valueOf(node.getProperty("unread").toString()));

                messageList.add(messageEntity);
            }

            t.success();
        }
        return messageList;
    }

    @Override
    public int getCountOfUnreadReceivedMessagesOfPlayer(Long playerId) {
        if (playerId == null) {
            throw new IllegalArgumentException("Player Id can not be null.");
        }

        GraphDatabaseService gds = DbConnector.getDBConnection();
        ExecutionEngine engine = DbConnector.getEngine();

        try (Transaction t = gds.beginTx()) {

            Map<String, Object> params = new HashMap<>();

            params.put("id", playerId);

            String query = "MATCH (Player:Player {id : {id}})-[r:RECIEVED]->(Message:Message {unread : true}) RETURN count(Message)";

            Long key = new Long(0);
            ExecutionResult result = engine.execute(query, params);
            Iterator<Long> number = result.columnAs("count(Message)");
            while (number.hasNext()) {
                key = number.next();
            }
            t.success();

            return key.intValue();
        }
    }
}
